Skip to content

Conversation

@mohammadalfaiyazbitgo
Copy link
Contributor

No description provided.

This comment was marked as outdated.

@mohammadalfaiyazbitgo mohammadalfaiyazbitgo force-pushed the WP-5257/create-durable-nonce-solana-recovery branch from 7ee0805 to 91f0ce7 Compare July 15, 2025 20:26
@mohammadalfaiyazbitgo mohammadalfaiyazbitgo marked this pull request as ready for review July 15, 2025 20:27

This comment was marked as outdated.

let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>;
if (sdkCoin.getFamily() === CoinFamily.SOL) {
const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions;
console.log(params);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use logger

let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>;
if (sdkCoin.getFamily() === CoinFamily.SOL) {
const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions;
console.log(params);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^

walletContractAddress: '',
unsignedSweepPrebuildTx: undefined,
coinSpecificParams: undefined,
coinSpecificParams: coinSpecificParams?.solanaRecoveryOptions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kinda oddd to have solanaRecoveryOptions for handleEddsaRecovery.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah but for now it's the only coin with additional options, so kept it like this.

Comment on lines +57 to +64
// Solana specific recovery parameters
solanaRecoveryOptions: t.partial({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these solana specific, or eddsa specific? or is a subset of these for solana only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

solana specific.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woudnt this be confusing then? what would be the expected params for non-sol eddsa recoveries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export interface MPCRecoveryOptions {
  userKey?: string; // Box A
  backupKey?: string; // Box B
  bitgoKey: string; // Box C - this is bitgo's xpub and will be used to derive their root address
  recoveryDestination: string;
  walletPassphrase?: string;
  seed?: string;
  index?: number;
  // the starting receive address index to scan from for WRW recovery
  startingScanIndex?: number;
  // the number of addresses to scan from the starting index
  scan?: number;
  // token contract address of the token to recover
  tokenContractAddress?: string;
  apiKey?: string;
}```
that's the recovery options for other eddsa coins, so those options are already encapsulated in our base parameters. Only sol has additional options. 

};
let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>;
if (sdkCoin.getFamily() === CoinFamily.SOL) {
const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we not using params.solanaSpecificParams that u added here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

poor structuring of the parameters

it's passed in as

          coinSpecificParams: coinSpecificParams?.solanaRecoveryOptions,

@mohammadalfaiyazbitgo mohammadalfaiyazbitgo force-pushed the WP-5257/create-durable-nonce-solana-recovery branch from 91f0ce7 to 50a2edb Compare July 17, 2025 19:04
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements durable nonce support for Solana recovery and restructures the coin-specific parameters for recovery operations. The changes include creating reusable transaction utilities, standardizing error handling, and expanding parameter validation for different coin types.

Key changes:

  • Adds transaction utility functions for handling various transaction formats
  • Implements structured coin-specific recovery parameters for UTXO, EVM, and Solana coins
  • Introduces comprehensive error handling classes with appropriate HTTP status codes
  • Adds support for Solana durable nonce in recovery operations

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/shared/transactionUtils.ts New utility module for transaction type checking and data extraction
src/shared/errors.ts New custom error classes with HTTP status code mapping
src/shared/responseHandler.ts Updates error handling to support new custom error types
src/api/master/routers/masterApiSpec.ts Restructures API spec with coin-specific recovery parameters
src/api/master/handlers/recoveryWallet.ts Implements parameter validation and Solana durable nonce support
src/api/master/clients/enclavedExpressClient.ts Refactors to use new transaction utility functions
src/tests/api/master/recoveryWallet.test.ts Comprehensive test coverage for parameter validation
masterBitgoExpress.json Updates JSON schema to reflect new parameter structure

});
};
let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>;
if (sdkCoin.getFamily() === CoinFamily.SOL) {
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type assertion without null checking could cause runtime errors. The coinSpecificParams could be undefined, and accessing properties on solanaParams would fail if it's undefined.

Suggested change
if (sdkCoin.getFamily() === CoinFamily.SOL) {
if (sdkCoin.getFamily() === CoinFamily.SOL) {
if (!params.coinSpecificParams) {
throw new ValidationError('coinSpecificParams is required for Solana recovery');
}

Copilot uses AI. Check for mistakes.
txRequest.signableHex = firstTransaction.unsignedTx?.serializedTx || '';
txRequest.derivationPath = firstTransaction.unsignedTx?.derivationPath || '';
} else {
throw new Error(`Unrecognized transaction ${JSON.stringify(tx)}`);
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.stringify on untrusted input could potentially expose sensitive data or cause performance issues with large objects. Consider logging a generic error message instead.

Suggested change
throw new Error(`Unrecognized transaction ${JSON.stringify(tx)}`);
throw new Error('Unrecognized transaction format.');

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error would be thrown back to client, not a concern.

* Type guard to check if the object is an MPCTxs format
* MPCTxs format has a txRequests array containing transaction information
*/
export function isMPCTxs(tx: any): tx is MPCTxs {
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using 'any' type reduces type safety. Consider using 'unknown' instead to force proper type checking at call sites.

Suggested change
export function isMPCTxs(tx: any): tx is MPCTxs {
export function isMPCTxs(tx: unknown): tx is MPCTxs {

Copilot uses AI. Check for mistakes.
}

export function isMPCUnsignedTx(tx: any): tx is MPCUnsignedTx {
return 'unsignedTx' in tx && isMPCTx(tx);
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type guard logic is incorrect. isMPCUnsignedTx should check for 'unsignedTx' property but then calls isMPCTx which checks for 'signableHex'. An MPCUnsignedTx likely has a different structure than MPCTx.

Suggested change
return 'unsignedTx' in tx && isMPCTx(tx);
return tx && 'unsignedTx' in tx && typeof tx.unsignedTx === 'object';

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +120 to +131
const solanaRecoveryOptions: SolRecoveryOptions = { ...options };
solanaRecoveryOptions.recoveryDestinationAtaAddress =
solanaParams.recoveryDestinationAtaAddress;
solanaRecoveryOptions.closeAtaAddress = solanaParams.closeAtaAddress;
solanaRecoveryOptions.tokenContractAddress = solanaParams.tokenContractAddress;
solanaRecoveryOptions.programId = solanaParams.programId;
if (solanaParams.durableNonce) {
solanaRecoveryOptions.durableNonce = {
publicKey: solanaParams.durableNonce.publicKey,
secretKey: solanaParams.durableNonce.secretKey,
};
}
Copy link

Copilot AI Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The spread operator creates a shallow copy, but then individual properties are assigned. Consider directly constructing the object to be more explicit about the intended structure.

Suggested change
const solanaRecoveryOptions: SolRecoveryOptions = { ...options };
solanaRecoveryOptions.recoveryDestinationAtaAddress =
solanaParams.recoveryDestinationAtaAddress;
solanaRecoveryOptions.closeAtaAddress = solanaParams.closeAtaAddress;
solanaRecoveryOptions.tokenContractAddress = solanaParams.tokenContractAddress;
solanaRecoveryOptions.programId = solanaParams.programId;
if (solanaParams.durableNonce) {
solanaRecoveryOptions.durableNonce = {
publicKey: solanaParams.durableNonce.publicKey,
secretKey: solanaParams.durableNonce.secretKey,
};
}
const solanaRecoveryOptions: SolRecoveryOptions = {
bitgoKey: options.bitgoKey,
recoveryDestination: options.recoveryDestination,
apiKey: options.apiKey,
recoveryDestinationAtaAddress: solanaParams.recoveryDestinationAtaAddress,
closeAtaAddress: solanaParams.closeAtaAddress,
tokenContractAddress: solanaParams.tokenContractAddress,
programId: solanaParams.programId,
durableNonce: solanaParams.durableNonce
? {
publicKey: solanaParams.durableNonce.publicKey,
secretKey: solanaParams.durableNonce.secretKey,
}
: undefined,
};

Copilot uses AI. Check for mistakes.
@mohammadalfaiyazbitgo mohammadalfaiyazbitgo merged commit fd128b6 into master Jul 17, 2025
3 checks passed
@mohammadalfaiyazbitgo mohammadalfaiyazbitgo deleted the WP-5257/create-durable-nonce-solana-recovery branch July 17, 2025 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants